#coding:utf-8
"""
Auth: 张宇飞(zhangyufei49@163.com)
Date: 2017-06-14
Desc: biu的服务端。用于提供穿洞服务。
"""
import socket
######################| 工具 |######################
LOCAL_HOST = "127.0.0.1"

def make_cmd(args):
    for i in xrange(len(args)):
        args[i] = str(args[i])
    return "#".join(args)

def split_cmd(data):
    res = data.split("#")
    if data[-1] == "#":
        res.pop()
    return res

def is_srv(addr):
    return addr[0] == LOCAL_HOST

######################| MainService |######################

class MainService(object):
    """docstring for MainService"""
    def __init__(self):
        super(MainService, self).__init__()
        self.sock = None
        self.handlers = {
                "hi": self.client_hi,
                "come": self.client_come,
                "gone": self.client_gone,
                "sayhi": self.say_hi,
                "echo": self.client_echo, # 用于测试服务器可用性
                "list": self.list_clients, # 用于打印当前客户端列表
                "file": self.transfer_file,
                }

        # 每个元素的格式 ip: [name, isLive, addr]
        self.clients = {}
        self.running = False

    def start(self, port):
        addr = ("", port)
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.bind(addr)
        self.sock = sock

        print "main service is running"
        self.running = True
        sock.settimeout(5)
        while self.running:
            try:
                data, client_addr = sock.recvfrom(2048)
            except socket.timeout:
                continue
            except Exception as e:
                print e.message
                break
            data = data.strip()
            if not data:
                continue
            args = split_cmd(data)
            if len(args) < 1:
                continue
            key = args.pop(0)
            handler = self.handlers.get(key, None)
            if handler != None:
                handler(client_addr, args)

        sock.close()
        self.sock = None

    def stop(self):
        self.broadcast(make_cmd(['dead']))
        self.running = False
        print "main service stopped"

    def send(self, addr, cmd):
        if self.sock is None:
            return
        self.sock.sendto(cmd, addr)

    def broadcast(self, cmd, except_ip = None, before_send = None):
        for ip in self.clients:
            if ip != except_ip:
                if before_send != None:
                    before_send(ip)
                self.send(self.clients[ip][2], cmd)

    def list_clients(self, addr, args):
        if not is_srv(addr):
            return

        for ip in self.clients:
            print self.clients[ip]

    def transfer_file(self, addr, args):
        argc = len(args)
        if argc < 1:
            return
        dest_ip = args[0]
        dest = self.clients.get(dest_ip, None)
        if dest is None:
            return
        dest_addr = dest[2]

        cmd = ['file']
        if argc == 4:
            # 发起端的请求
            cmd.append(addr[0])

        cmd.extend(args[1:])
        self.send(dest_addr, make_cmd(cmd))


    def client_echo(self, addr, args):
        if not is_srv(addr):
            return

        self.send(addr, " ".join(args))

    def client_hi(self, addr, args):
        c = self.clients.get(addr[0], None)
        if c != None:
            c[1] = True

    def client_come(self, addr, args):
        if len(args) < 1:
            self.send(addr, make_cmd(['no', '添加新用户参数错误']))
            return

        ip = addr[0]
        # 告诉你其它用户都谁
        others = ['ok']
        for c in self.clients:
            if c != ip:
                others.append(c)
                others.append(self.clients[c][0])

        name = args[0]
        self.clients[ip] = [name, True, addr]
        print "client [%s] %s:%d is comming" % (name, addr[0], addr[1])

        self.send(addr, make_cmd(others))

        # 通知其它用户你来了
        self.broadcast(make_cmd(['come', ip, name]), ip)

    def client_gone(self, addr, args):
        ip = addr[0]
        if not self.clients.has_key(ip):
            return
        self.clients.pop(ip)
        self.send(addr, make_cmd(['dead']))

        # 通知其它用户你走了
        self.broadcast(make_cmd(["gone", ip]))

    def say_hi(self, addr, args):
        # 找到没有响应心跳的，清除
        torem_ip = []
        for ip in self.clients:
            if not self.clients[ip][1]:
                torem_ip.append(ip)

        if len(torem_ip):
            for ip in torem_ip:
                c = self.clients.pop(ip)
                self.send(c[2], make_cmd(['dead']))

            torem_ip.insert(0, 'gone')
            self.broadcast(make_cmd(torem_ip))

        # 向剩下的广播心跳
        def before_send(ip):
            self.clients[ip][1] = False

        self.broadcast(make_cmd(["hi"]), None, before_send)

######################| HeartbeatService |######################

import threading
class HeartbeatService(threading.Thread):
    """定时的通过socket发送一个消息给server让它执行心跳"""
    def __init__(self, port, timeout):
        super(HeartbeatService, self).__init__()
        # 启动线程
        self.port = port
        self.timeout = timeout
        self.running = False

    def run(self):
        print "heartbeat service is running"
        self.running = True
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        con = (LOCAL_HOST, self.port)
        cmd = make_cmd(["sayhi"])
        timeout = self.timeout
        cond = threading.Condition()
        self.cond = cond

        cond.acquire()
        while self.running:
            cond.wait(timeout)
            sock.sendto(cmd, con)
        cond.release()

        sock.close()
        print "heartbeat service is stopped"

    def stop(self):
        self.running = False
        cond = self.cond
        if cond is not None:
            cond.acquire()
            cond.notify()
            cond.release()

######################| main |######################

def signal_handler(sig, action):
    for service in services:
        service.stop()

def main(services):
    from sys import argv
    if len(argv) < 3:
        print "usage: %s <port> <heart beat time out secs>" % argv[0]
        return

    port = int(argv[1])
    heartbeat_timeout = int(argv[2])

    main_service = MainService()
    services.append(main_service)

    heartbeat_service = HeartbeatService(port, heartbeat_timeout)
    services.append(heartbeat_service)

    heartbeat_service.start()
    main_service.start(port)

    heartbeat_service.join()

if __name__ == "__main__":
    services = []

    # 注册信号处理函数
    import signal
    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)

    main(services)
